package io.hellsinger.vortex.ui.component.adapter

import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.hellsinger.vortex.ui.component.adapter.holder.RecyclableView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

abstract class MultipleTypeAdapter : RecyclerView.Adapter<MultipleTypeAdapter.MultipleViewHolder>() {
    protected val list = mutableListOf<SuperItem>()

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int,
    ): MultipleViewHolder = MultipleViewHolder(createView(parent, viewType))

    override fun getItemId(position: Int): Long = list[position].id

    fun replace(data: List<SuperItem>) =
        CoroutineScope(Dispatchers.Main.immediate + SupervisorJob()).launch {
            if (data == list) return@launch

            if (data.isEmpty()) {
                val count = itemCount
                list.clear()
                notifyItemRangeRemoved(0, count)
                return@launch
            }

            val differ =
                createDiffer(data) ?: run {
                    list.clear()
                    list.addAll(data)
                    notifyDataSetChanged()
                    return@launch
                }
            val result =
                withContext(Dispatchers.Default) {
                    DiffUtil.calculateDiff(differ)
                }
            list.clear()
            list.addAll(data)

            result.dispatchUpdatesTo(this@MultipleTypeAdapter)
        }

    protected abstract fun createDiffer(new: List<Item<*>>): DiffUtil.Callback?

    protected abstract fun createView(
        parent: ViewGroup,
        viewType: Int,
    ): View

    override fun getItemCount(): Int = list.size

    override fun getItemViewType(position: Int): Int = list[position].viewType

    class MultipleViewHolder(
        val view: View,
    ) : RecyclerView.ViewHolder(view)

    protected inline fun <reified V, T> bind(
        holder: MultipleViewHolder,
        item: SuperItem,
    ) where V : View, V : RecyclableView<T> {
        val root = holder.view as V
        root.tag = holder.bindingAdapterPosition

        root.onBind(item.value as T)
    }

    protected inline fun <reified V : RecyclableView<T>, T> unbind(holder: MultipleViewHolder) {
        val root = holder.view as V

        root.onUnbind()
    }
}
